package vision

import (
	"context"
	"strings"
	"sync"

	"github.com/photoprism/photoprism/pkg/http/scheme"
)

// ModelEngine represents the canonical identifier for a computer vision service engine.
type ModelEngine = string

const (
	// EngineVision represents the default PhotoPrism vision service endpoints.
	EngineVision ModelEngine = "vision"
	// EngineTensorFlow represents on-device TensorFlow models.
	EngineTensorFlow ModelEngine = "tensorflow"
	// EngineLocal is used when no explicit engine can be determined.
	EngineLocal ModelEngine = "local"
)

// RequestBuilder builds an API request for an engine based on the model configuration and input files.
type RequestBuilder interface {
	Build(ctx context.Context, model *Model, files Files) (*ApiRequest, error)
}

// ResponseParser parses a raw engine response into the generic ApiResponse structure.
type ResponseParser interface {
	Parse(ctx context.Context, req *ApiRequest, raw []byte, status int) (*ApiResponse, error)
}

// EngineDefaults supplies engine-specific prompt and schema defaults when they are not configured explicitly.
type EngineDefaults interface {
	SystemPrompt(model *Model) string
	UserPrompt(model *Model) string
	SchemaTemplate(model *Model) string
	Options(model *Model) *ModelOptions
}

// Engine groups the callbacks required to integrate a third-party vision service.
type Engine struct {
	Builder  RequestBuilder
	Parser   ResponseParser
	Defaults EngineDefaults
}

var (
	engineRegistry   = make(map[ApiFormat]Engine)
	engineAliasIndex = make(map[string]EngineInfo)
	engineMu         sync.RWMutex
)

// init wires up the built-in aliases so configuration files can reference the
// human-friendly engine names without duplicating adapter metadata.
func init() {
	RegisterEngineAlias(EngineVision, EngineInfo{
		RequestFormat:     ApiFormatVision,
		ResponseFormat:    ApiFormatVision,
		FileScheme:        scheme.Data,
		DefaultResolution: DefaultResolution,
	})
}

// RegisterEngine adds/overrides an engine implementation for a specific API format.
func RegisterEngine(format ApiFormat, engine Engine) {
	engineMu.Lock()
	defer engineMu.Unlock()
	engineRegistry[format] = engine
}

// EngineInfo describes metadata that can be associated with an engine alias.
type EngineInfo struct {
	Uri               string
	RequestFormat     ApiFormat
	ResponseFormat    ApiFormat
	FileScheme        string
	DefaultModel      string
	DefaultResolution int
	DefaultKey        string // Optional placeholder key (e.g., ${OPENAI_API_KEY}); applied only when Service.Key is empty.
}

// RegisterEngineAlias maps a logical engine name (e.g., "ollama") to a
// request/response format pair.
func RegisterEngineAlias(name string, info EngineInfo) {
	name = strings.TrimSpace(strings.ToLower(name))
	if name == "" || info.RequestFormat == "" {
		return
	}

	if info.ResponseFormat == "" {
		info.ResponseFormat = info.RequestFormat
	}

	engineMu.Lock()
	engineAliasIndex[name] = info
	engineMu.Unlock()
}

// EngineInfoFor returns the metadata associated with a logical engine name.
func EngineInfoFor(name string) (EngineInfo, bool) {
	name = strings.TrimSpace(strings.ToLower(name))
	engineMu.RLock()
	info, ok := engineAliasIndex[name]
	engineMu.RUnlock()
	return info, ok
}

// EngineFor returns the registered engine implementation for the given API
// format, if any.
func EngineFor(format ApiFormat) (Engine, bool) {
	engineMu.RLock()
	defer engineMu.RUnlock()
	engine, ok := engineRegistry[format]
	return engine, ok
}
